/***************************************************************************//**
 * @file MSc_Vector.h
 * @author  Marek M. Cel <marekcel@mscsim.org>
 *
 * @section LICENSE
 *
 * Copyright (C) 2013 Marek M. Cel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
#ifndef MSC_VECTOR_H
#define MSC_VECTOR_H

////////////////////////////////////////////////////////////////////////////////

#include <cmath>
#include <sstream>

#include "MSc_Exception.h"
#include "MSc_Utils.h"

////////////////////////////////////////////////////////////////////////////////

/**	Column vector base class template. */
template <int SIZE>
class MSc_Vector
{
public:

    /** Constructor. */
    MSc_Vector() :
        m_size ( SIZE )
    {
        double * item = m_items;

        for ( int i = 0; i < m_size; i++ )
        {
            (*item) = 0.0; item++; // m_items[ i ] = 0.0;
        }
    }

    /** Copy constructor. */
    MSc_Vector( const MSc_Vector<SIZE> &vect ) :
        m_size ( SIZE )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] = vect.m_items[ i ];
        }
    }

    /** @return TRUE if all items are valid */
    virtual bool isValid() const
    {
        return MSc_Utils::isValid( m_items, m_size );
    }

    /** @return vector length squared */
    virtual double getLength2() const
    {
        double length2 = 0.0;

        for ( int i = 0; i < m_size; i++ )
        {
            length2 += ( m_items[ i ] * m_items[ i ] );
        }

        return length2;
    }

    /** @return vector length */
    virtual double getLength() const
    {
        return sqrt( getLength2() );
    }

    /** This function normalizes vector. */
    void normalize()
    {
        double length = getLength();

        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] = m_items[ i ] / length;
        }
    }

    /** Puts vector items into given array. */
    virtual void getArray( double items[] ) const
    {
        for ( int i = 0; i < m_size; i++ )
        {
            items[i] = m_items[i];
        }
    }

    /**
     * This function is bound-checked which may affect performance.
     * Throws MSc_Exception when index is out of range.
     * @return vector item of given indicies. */
    virtual double getItem( int index ) const
    {
        if ( ( index < 0 ) || ( index >= m_size ) )
        {
            MSc_Exception e;

            e.setType( MSc_Exception::ArrayIndexOverLimit );
            e.setFile( __FILE__ );
            e.setLine( __LINE__ );
            e.setInfo( "Array index over limit." );

            throw e;
        }

        return m_items[ index ];
    }

    /** Sets vector items from given array. */
    virtual void setArray( const double items[] )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[i] = items[i];
        }
    }

    /**
     * This function sets vector item of given indicies.
     * This function is bound-checked which may affect performance.
     * Throws MSc_Exception when index is out of range. */
    virtual void setItem( int index, double val )
    {
        if ( ( index < 0 ) || ( index >= m_size ) )
        {
            MSc_Exception e;

            e.setType( MSc_Exception::ArrayIndexOverLimit );
            e.setFile( __FILE__ );
            e.setLine( __LINE__ );
            e.setInfo( "Array index over limit." );

            throw e;
        }

        m_items[ index ] = val;
    }

    /** This function returns string represtation of the vector. */
    virtual std::string toString()
    {
        std::stringstream ss;

        for ( int i = 0; i < m_size; i++ )
        {
            if ( i != 0 ) ss << ",";

            ss << m_items[ i ];
        }

        return ss.str();
    }

    /**
     * Items accessor. Please notice that this operator is NOT bound-checked.
     * If you want bound-checked item accessor use getItem(int) or
     * setItem(int,double) functions. */
    inline double operator() ( int index ) const
    {
#       ifdef _DEBUG
        if ( ( index < 0 ) || ( index >= m_size ) )
        {
            MSc_Exception e;

            e.setType( MSc_Exception::ArrayIndexOverLimit );
            e.setFile( __FILE__ );
            e.setLine( __LINE__ );
            e.setInfo( "Array index over limit." );

            throw e;
        }
#       endif

        return m_items[ index ];
    }

    /**
     * Items accessor. Please notice that this operator is NOT bound-checked.
     * If you want bound-checked item accessor use getItem(int) or
     * setItem(int,double) functions. */
    inline double& operator() ( int index )
    {
#       ifdef _DEBUG
        if ( ( index < 0 ) || ( index >= m_size ) )
        {
            MSc_Exception e;

            e.setType( MSc_Exception::ArrayIndexOverLimit );
            e.setFile( __FILE__ );
            e.setLine( __LINE__ );
            e.setInfo( "Array index over limit." );

            throw e;
        }
#       endif

        return m_items[ index ];
    }

    /** Assignment operator. */
    MSc_Vector<SIZE>& operator= ( const MSc_Vector<SIZE> &vect )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[i] = vect.m_items[i];
        }

        return (*this);
    }

    /** Addition operator. */
    MSc_Vector<SIZE> operator+ ( const MSc_Vector<SIZE> &vect ) const
    {
        MSc_Vector<SIZE> result;

        for ( int i = 0; i < m_size; i++ )
        {
            result.m_items[ i ] = m_items[ i ] + vect.m_items[ i ];
        }

        return result;
    }

    /** Negation operator. */
    MSc_Vector<SIZE> operator- () const
    {
        MSc_Vector<SIZE> result;

        for ( int i = 0; i < m_size; i++ )
        {
            result.m_items[ i ] = - m_items[ i ];
        }

        return result;
    }

    /** Subtraction operator. */
    MSc_Vector<SIZE> operator- ( const MSc_Vector<SIZE> &vect ) const
    {
        MSc_Vector<SIZE> result;

        for ( int i = 0; i < m_size; i++ )
        {
            result.m_items[ i ] = m_items[ i ] - vect.m_items[ i ];
        }

        return result;
    }

    /** Multiplication operator (by scalar). */
    MSc_Vector<SIZE> operator* ( double val ) const
    {
        MSc_Vector<SIZE> result;

        for ( int i = 0; i < m_size; i++ )
        {
            result.m_items[ i ] = m_items[ i ] * val;
        }

        return result;
    }

    /** Dot product operator. */
    double operator* ( const MSc_Vector<SIZE> &vect ) const
    {
        double result = 0.0;

        for ( int i = 0; i < m_size; i++ )
        {
            result += m_items[ i ] * vect.m_items[ i ];
        }

        return result;
    }

    /** Division operator (by scalar). */
    MSc_Vector<SIZE> operator/ ( double val ) const
    {
        MSc_Vector<SIZE> result;

        for ( int i = 0; i < m_size; i++ )
        {
            result.m_items[ i ] = m_items[ i ] / val;
        }

        return result;
    }

    /** Unary addition operator. */
    MSc_Vector<SIZE>& operator+= ( const MSc_Vector<SIZE> &vect )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] += vect.m_items[ i ];
        }

        return (*this);
    }

    /** Unary subtraction operator. */
    MSc_Vector<SIZE>& operator-= ( const MSc_Vector<SIZE> &vect )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] -= vect.m_items[ i ];
        }

        return (*this);
    }

    /** Unary multiplication operator (by scalar). */
    MSc_Vector<SIZE>& operator*= ( double val )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] *= val;
        }

        return (*this);
    }

    /** Unary division operator (by scalar). */
    MSc_Vector<SIZE>& operator/= ( double val )
    {
        for ( int i = 0; i < m_size; i++ )
        {
            m_items[ i ] /= val;
        }

        return (*this);
    }

    /** Equality operator. */
    bool operator== ( const MSc_Vector<SIZE> &vect ) const
    {
        bool result = true;

        for ( int i = 0; i < m_size; i++ )
        {
            result = result && ( m_items[i] == vect.m_items[i] );
        }

        return result;
    }

    /** Inequality operator. */
    bool operator!= ( const MSc_Vector<SIZE> &vect ) const
    {
        return !( (*this) == vect );
    }

protected:

    double m_items[ SIZE ];     ///< vector items

    const int m_size;           ///< vector size
};

////////////////////////////////////////////////////////////////////////////////

#endif // MSC_VECTOR_H
